#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include "onvif_comm.h"
#include "onvif_dump.h"
#include "soapStub.h"
#ifdef WIN32
#include <windows.h>
#endif

/* 遮挡报警 */
// #define TAMPER_TOPIC            "tns1:RuleEngine/TamperDetector/Tamper"
// #define TAMPER_NAME             "IsTamper"
// #define TAMPER_VALUE            "true"

const char* TAMPER_TOPIC =     "tns1:RuleEngine/TamperDetector/Tamper";
const char* TAMPER_NAME  =     "IsTamper";
const char* TAMPER_VALUE =     "true";

/************************************************************************
**函数：find_event
**功能：查找指定主题、指定内容的事件
**参数：略
**返回：
        0表明未找到，非0表明找到
************************************************************************/
int find_event(struct _tev__PullMessagesResponse *rep, const char *topic, const char *name, const char *value)
{
    int i, j;

    if(NULL == rep) {
        return 0;
    }

    for (i = 0; i < rep->__sizeNotificationMessage; i++) {
        struct wsnt__NotificationMessageHolderType *p = rep->wsnt__NotificationMessage + i;

        if (NULL == p->Topic) {
            continue;
        }
        if (NULL == p->Topic->__mixed ) {
            continue;
        }
        if (0 != strcmp(topic, p->Topic->__mixed)) {
            continue;
        }

        if (NULL == p->Message.tt__Message) {
            continue;
        }
        if (NULL == p->Message.tt__Message->Data) {
            continue;
        }
        if (NULL == p->Message.tt__Message->Data->SimpleItem) {
            continue;
        }
        for (j = 0; j < p->Message.tt__Message->Data->__sizeSimpleItem; j++) {
            struct _tt__ItemList_SimpleItem *a = p->Message.tt__Message->Data->SimpleItem + j;
            if (NULL == a->Name || NULL == a->Value) {
                continue;
            }
            if (0 != strcmp(name, a->Name)) {
                continue;
            }
            if (0 != strcmp(value, a->Value)) {
                continue;
            }

            return 1;
        }
    }


    return 0;
}

/************************************************************************
**函数：IsTamper
**功能：判断是否有遮挡报警
**参数：略
**返回：
        0表明没有，非0表明有
************************************************************************/
int IsTamper(struct _tev__PullMessagesResponse *rep)
{
    return find_event(rep, TAMPER_TOPIC, TAMPER_NAME, TAMPER_VALUE);
}

/************************************************************************
**函数：ONVIF_CreatePullPointSubscription
**功能：使用Pull-Point方式订阅事件
**参数：
        [in] EventXAddr - 事件服务地址
**返回：
        0表明成功，非0表明失败
************************************************************************/
int ONVIF_CreatePullPointSubscription(const char *EventXAddr)
{
    int i;
    int result = 0;
    struct soap *soap = NULL;
    char *pullpoint = NULL;
    struct _tev__CreatePullPointSubscription         req;
    struct _tev__CreatePullPointSubscriptionResponse rep;

    struct _tev__PullMessages                        req_pm;
    struct _tev__PullMessagesResponse                rep_pm;

    struct _wsnt__Unsubscribe                        req_u;
    struct _wsnt__UnsubscribeResponse                rep_u;

#if 0
    #define PULLMSG_TIMEOUT_UNIT    (1000)// 海康IPC单位
#else
    #define PULLMSG_TIMEOUT_UNIT    (5000000)// 大华IPC单位
#endif

    LONG64 pullmsg_timeout = 5 * PULLMSG_TIMEOUT_UNIT;// PullMessages查询事件的超时时间，不同IPC厂家的单位不同
    int socket_timeout = 10; // 创建soap的socket超时时间，单位秒

    SOAP_ASSERT(pullmsg_timeout < socket_timeout * PULLMSG_TIMEOUT_UNIT);// 要确保查询事件的超时时间比socket超时时间小，否则，事件没查询到就socket超时，导致PullMessages返回失败
    SOAP_ASSERT(NULL != EventXAddr);
    SOAP_ASSERT(NULL != (soap = ONVIF_soap_new(socket_timeout)));

    /*
    *
    * 可以通过主题过滤我们所订阅的事件，过滤规则在官方「ONVIF Core Specification」规格说明书「Topic Filter」章节里有详细的介绍。
    * 比如：
    * tns1:RuleEngine/TamperDetector/Tamper   只关心遮挡报警
    * tns1:RuleEngine/TamperDetector//.       只关心主题TamperDetector树下的事件
    * NULL                                    关心所有事件，即不过滤
    *                                         也可以通过 '|' 表示或的关系，即同时关心某几类事件
    *
    */
    memset(&req, 0x00, sizeof(req));// 订阅事件
    memset(&rep, 0x00, sizeof(rep));
    req.Filter = (struct wsnt__FilterType *)ONVIF_soap_malloc(soap, sizeof(struct wsnt__FilterType));
    req.Filter->TopicExpression = (struct wsnt__TopicExpressionType *)ONVIF_soap_malloc(soap, sizeof(struct wsnt__TopicExpressionType));
    req.Filter->TopicExpression->Dialect = "http://www.onvif.org/ver10/tev/topicExpression/ConcreteSet";
    req.Filter->TopicExpression->__mixed = TAMPER_TOPIC;
    ONVIF_SetAuthInfo(soap, USERNAME, PASSWORD);
    result = soap_call___tev__CreatePullPointSubscription(soap, EventXAddr, NULL, &req, &rep);
    SOAP_CHECK_ERROR(result, soap, "CreatePullPointSubscription");
    dump_tev__CreatePullPointSubscriptionResponse(&rep);

    pullpoint = rep.SubscriptionReference.Address;// 提取pull point地址

    for (i = 0; i < 30; i++) {  // 轮询事件
        memset(&req_pm, 0x00, sizeof(req_pm));
        memset(&rep_pm, 0x00, sizeof(rep_pm));
        req_pm.Timeout      = pullmsg_timeout;
        req_pm.MessageLimit = 0;
        ONVIF_SetAuthInfo(soap, USERNAME, PASSWORD);
        result = soap_call___tev__PullMessages(soap, pullpoint, NULL, &req_pm, &rep_pm);
        SOAP_CHECK_ERROR(result, soap, "PullMessages");
        dump_tev__PullMessagesResponse(&rep_pm);

        if(IsTamper(&rep_pm)) { // 是遮挡报警?
            SOAP_DBGLOG("Tamper...\n");
        }
    }

EXIT:

    memset(&req_u, 0x00, sizeof(req_u)); // 退订事件
    memset(&rep_u, 0x00, sizeof(rep_u));
    ONVIF_SetAuthInfo(soap, USERNAME, PASSWORD);
    result = soap_call___tev__Unsubscribe(soap, pullpoint, NULL, &req_u, &rep_u);
    if (SOAP_OK != result || SOAP_OK != soap->error) {
        soap_perror(soap, "Unsubscribe");
        if (SOAP_OK == result) {
            result = soap->error;
        }
    }

    if (NULL != soap) {
        ONVIF_soap_delete(soap);
    }

    return result;
}

/************************************************************************
**函数：ONVIF_GetEventProperties
**功能：获取事件属性
**参数：
        [in] EventXAddr - 事件服务地址
**返回：
        0表明成功，非0表明失败
************************************************************************/
int ONVIF_GetEventProperties(const char *EventXAddr)
{
    int result = 0;
    struct soap *soap = NULL;
    struct _tev__GetEventProperties         req;
    struct _tev__GetEventPropertiesResponse rep;

    SOAP_ASSERT(NULL != EventXAddr);
    SOAP_ASSERT(NULL != (soap = ONVIF_soap_new(SOAP_SOCK_TIMEOUT)));

    memset(&req, 0x00, sizeof(req));
    memset(&rep, 0x00, sizeof(rep));
    ONVIF_SetAuthInfo(soap, USERNAME, PASSWORD);
    result = soap_call___tev__GetEventProperties(soap, EventXAddr, NULL, &req, &rep);
    SOAP_CHECK_ERROR(result, soap, "GetEventProperties");
    dump_tev__GetEventPropertiesResponse(&rep);

EXIT:

    if (NULL != soap) {
        ONVIF_soap_delete(soap);
    }

    return result;
}

/************************************************************************
**函数：ONVIF_GetServiceCapabilities
**功能：获取服务功能
**参数：
        [in] EventXAddr - 事件服务地址
**返回：
        0表明成功，非0表明失败
************************************************************************/
int ONVIF_GetServiceCapabilities(const char *EventXAddr)
{
    int result = 0;
    struct soap *soap = NULL;
    struct _tev__GetServiceCapabilities         req;
    struct _tev__GetServiceCapabilitiesResponse rep;

    SOAP_ASSERT(NULL != EventXAddr);
    SOAP_ASSERT(NULL != (soap = ONVIF_soap_new(SOAP_SOCK_TIMEOUT)));

    memset(&req, 0x00, sizeof(req));
    memset(&rep, 0x00, sizeof(rep));
    ONVIF_SetAuthInfo(soap, USERNAME, PASSWORD);
    result = soap_call___tev__GetServiceCapabilities(soap, EventXAddr, NULL, &req, &rep);
    SOAP_CHECK_ERROR(result, soap, "GetServiceCapabilities");
    dump_tev__GetServiceCapabilitiesResponse(&rep);

EXIT:

    if (NULL != soap) {
        ONVIF_soap_delete(soap);
    }

    return result;
}

void cb_discovery(char *DeviceXAddr)
{
    struct tagCapabilities capa;

    ONVIF_GetCapabilities(DeviceXAddr, &capa);// 获取设备能力信息（获取媒体服务地址）
    ONVIF_GetServiceCapabilities(capa.EventXAddr);// 获取服务功能
    ONVIF_GetEventProperties(capa.EventXAddr);// 获取事件属性
    ONVIF_CreatePullPointSubscription(capa.EventXAddr);// 使用Pull-Point方式订阅事件
}

int main(int argc, char **argv)
{
    char *g_deviceXAddr=NULL;

    parse_options(argc, argv);

    if (NULL == g_deviceXAddr) 
    {
        ONVIF_DetectDevice(cb_discovery);
    } 
    else 
    {
        cb_discovery(g_deviceXAddr);
    }

    return 0;
}
